package com.sdhs.workflow.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.sdhs.common.core.domain.entity.SysDept;
import com.sdhs.common.core.domain.entity.SysRole;
import com.sdhs.common.core.domain.entity.SysUser;
import com.sdhs.common.utils.SecurityUtils;
import com.sdhs.common.utils.spring.SpringUtils;
import com.sdhs.common.utils.StreamUtils;
import com.sdhs.system.service.ISysDeptService;
import com.sdhs.system.service.ISysUserService;
import com.sdhs.system.domain.MessageInfoDO;
import com.sdhs.system.service.IMessageInfoService;
import com.sdhs.workflow.common.constant.FlowConstant;
import com.sdhs.workflow.common.enums.TaskStatusEnum;
import com.sdhs.workflow.domain.ActHiTaskinst;
import com.sdhs.workflow.domain.vo.MultiInstanceVo;
import com.sdhs.workflow.domain.vo.ParticipantVo;
import com.sdhs.workflow.domain.vo.TaskVo;
import com.sdhs.workflow.flowable.cmd.UpdateHiTaskInstCmd;
import com.sdhs.workflow.mapper.ActHiTaskinstMapper;
import com.sdhs.workflow.mapper.ActTaskMapper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;

import java.util.*;

/**
 * 工作流工具
 *
 * @author may
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class WorkflowUtils {

    private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
    private static final ActHiTaskinstMapper ACT_HI_TASKINST_MAPPER = SpringUtils.getBean(ActHiTaskinstMapper.class);
    private static final IMessageInfoService messageInfoService = SpringUtils.getBean(IMessageInfoService.class);
    private static final ISysUserService userService = SpringUtils.getBean(ISysUserService.class);
    private static final ActTaskMapper actTaskMapper = SpringUtils.getBean(ActTaskMapper.class);
    private static final ISysDeptService sysDeptService = SpringUtils.getBean(ISysDeptService.class);

    /**
     * 创建一个新任务
     *
     * @param currentTask 参数
     */
    public static TaskEntity createNewTask(Task currentTask) {
        TaskEntity task = null;
        if (ObjectUtil.isNotEmpty(currentTask)) {
            task = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask();
            task.setCategory(currentTask.getCategory());
            task.setDescription(currentTask.getDescription());
            task.setAssignee(currentTask.getAssignee());
            task.setName(currentTask.getName());
            task.setProcessDefinitionId(currentTask.getProcessDefinitionId());
            task.setProcessInstanceId(currentTask.getProcessInstanceId());
            task.setTaskDefinitionKey(currentTask.getTaskDefinitionKey());
            task.setPriority(currentTask.getPriority());
            task.setCreateTime(new Date());
            PROCESS_ENGINE.getTaskService().saveTask(task);
        }
        if (ObjectUtil.isNotNull(task)) {
            UpdateHiTaskInstCmd updateHiTaskInstCmd = new UpdateHiTaskInstCmd(Collections.singletonList(task.getId()), task.getProcessDefinitionId(), task.getProcessInstanceId());
            PROCESS_ENGINE.getManagementService().executeCommand(updateHiTaskInstCmd);
        }
        return task;
    }

    /**
     * 抄送任务
     *
     * @param parentTaskList 父级任务
     * @param userIds        人员id
     */
    public static void createCopyTask(List<Task> parentTaskList, List<String> userIds) {
        List<Task> list = new ArrayList<>();
        for (Task parentTask : parentTaskList) {
            for (String userId : userIds) {
                TaskEntity newTask = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask();
                newTask.setParentTaskId(parentTask.getId());
                newTask.setAssignee(userId);
                newTask.setName("【抄送】-" + parentTask.getName());
                newTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());
                newTask.setProcessInstanceId(parentTask.getProcessInstanceId());
                newTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
                list.add(newTask);
            }
        }
        PROCESS_ENGINE.getTaskService().bulkSaveTasks(list);
        if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(parentTaskList)) {
            String processInstanceId = parentTaskList.get(0).getProcessInstanceId();
            String processDefinitionId = parentTaskList.get(0).getProcessDefinitionId();
            List<String> taskIds = StreamUtils.toList(list, Task::getId);
            ActHiTaskinst actHiTaskinst = new ActHiTaskinst();
            actHiTaskinst.setProcDefId(processDefinitionId);
            actHiTaskinst.setProcInstId(processInstanceId);
            actHiTaskinst.setScopeType(TaskStatusEnum.COPY.getStatus());
            LambdaUpdateWrapper<ActHiTaskinst> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.in(ActHiTaskinst::getId, taskIds);
            ACT_HI_TASKINST_MAPPER.update(actHiTaskinst, updateWrapper);
            for (Task task : list) {
                PROCESS_ENGINE.getTaskService().addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), StrUtil.EMPTY);
            }
        }
    }

    /**
     * 获取当前任务参与者
     *
     * @param taskId 任务id
     */
    public static ParticipantVo getCurrentTaskParticipant(String taskId, ISysUserService userService) {
        ParticipantVo participantVo = new ParticipantVo();
        List<HistoricIdentityLink> linksForTask = PROCESS_ENGINE.getHistoryService().getHistoricIdentityLinksForTask(taskId);
        Task task = QueryUtils.taskQuery().taskId(taskId).singleResult();
        if (task != null && CollUtil.isNotEmpty(linksForTask)) {
            List<HistoricIdentityLink> groupList = StreamUtils.filter(linksForTask, e -> StringUtils.isNotBlank(e.getGroupId()));
            if (CollUtil.isNotEmpty(groupList)) {
                List<String> groupIds = StreamUtils.toList(groupList, e -> e.getGroupId());
                List<String> userIds = userService.selectUserIdsByRoleIds(groupIds, null);
                if (CollUtil.isNotEmpty(userIds)) {
                    participantVo.setGroupIds(groupIds);
                    List<SysUser> userList = userService.selectListByIds(userIds);
                    if (CollUtil.isNotEmpty(userList)) {
                        List<String> userIdList = StreamUtils.toList(userList, SysUser::getUserId);
                        List<String> nickNames = StreamUtils.toList(userList, SysUser::getNickName);
                        participantVo.setCandidate(userIdList);
                        participantVo.setCandidateName(nickNames);
                        participantVo.setClaim(!StringUtils.isBlank(task.getAssignee()));
                    }
                }
            } else {
                List<HistoricIdentityLink> candidateList = StreamUtils.filter(linksForTask, e -> FlowConstant.CANDIDATE.equals(e.getType()));
                List<String> userIdList = new ArrayList<>();
                for (HistoricIdentityLink historicIdentityLink : linksForTask) {
                    try {
                        userIdList.add(historicIdentityLink.getUserId());
                    } catch (NumberFormatException ignored) {

                    }
                }
                List<SysUser> userList = userService.selectListByIds(userIdList);
                if (CollUtil.isNotEmpty(userList)) {
                    List<String> userIds = StreamUtils.toList(userList, SysUser::getUserId);
                    List<String> nickNames = StreamUtils.toList(userList, SysUser::getNickName);
                    participantVo.setCandidate(userIds);
                    participantVo.setCandidateName(nickNames);
                    // 判断当前任务是否具有多个办理人
                    if (CollUtil.isNotEmpty(candidateList) && candidateList.size() > 1) {
                        // 如果 assignee 存在，则设置当前任务已经被认领
                        participantVo.setClaim(StringUtils.isNotBlank(task.getAssignee()));
                    }
                }
            }
        }
        return participantVo;
    }

    /**
     * 判断当前节点是否为会签节点
     *
     * @param processDefinitionId 流程定义id
     * @param taskDefinitionKey   流程定义id
     */
    public static MultiInstanceVo isMultiInstance(String processDefinitionId, String taskDefinitionKey) {
        BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
        FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
        MultiInstanceVo multiInstanceVo = new MultiInstanceVo();
        //判断是否为并行会签节点
        if (flowNode.getBehavior() instanceof ParallelMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) {
            Expression collectionExpression = behavior.getCollectionExpression();
            String assigneeList = collectionExpression.getExpressionText();
            String assignee = behavior.getCollectionElementVariable();
            multiInstanceVo.setType(behavior);
            multiInstanceVo.setAssignee(assignee);
            multiInstanceVo.setAssigneeList(assigneeList);
            return multiInstanceVo;
            //判断是否为串行会签节点
        } else if (flowNode.getBehavior() instanceof SequentialMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) {
            Expression collectionExpression = behavior.getCollectionExpression();
            String assigneeList = collectionExpression.getExpressionText();
            String assignee = behavior.getCollectionElementVariable();
            multiInstanceVo.setType(behavior);
            multiInstanceVo.setAssignee(assignee);
            multiInstanceVo.setAssigneeList(assigneeList);
            return multiInstanceVo;
        }
        return null;
    }

    /**
     * 获取当前流程状态
     *
     * @param taskId 任务id
     */
    public static String getBusinessStatusByTaskId(String taskId) {
        HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery().taskId(taskId).singleResult();
        HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(historicTaskInstance.getProcessInstanceId()).singleResult();
        return historicProcessInstance.getBusinessStatus();
    }

    /**
     * 获取当前流程状态
     *
     * @param businessKey 业务id
     */
    public static String getBusinessStatus(String businessKey) {
        HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult();
        return historicProcessInstance.getBusinessStatus();
    }

    /**
     * 根据任务信息查询需要通知的人员列表
     * @param list 任务
     * @param userService
     * @return
     */
    public static List<String> getUsersByTask(List<Task> list,ISysUserService userService)
    {
        Set<String> userIds = new HashSet<>();

        for (Task t : list) {
            ParticipantVo taskParticipant = WorkflowUtils.getCurrentTaskParticipant(t.getId(), userService);
            userIds.addAll(taskParticipant.getCandidate());
        }

        return new ArrayList<>(userIds);
    }


    /**
     *
     * @param list 任务
     * @param name 流程名称
     * @param userIds 需要发送给的用户集合
     * @param code 流程编码
     * @param creator 流程创建人id
     * @param creatorName 流程创建人姓名
     */
    public static void sendMessage(List<Task> list, String name, List<String> userIds,String code,String creator,String creatorName,String businessId) {
        sendMessage(list,name,userIds,code,creator,creatorName,businessId,null);
    }

    /**
     *
     * @param list 任务
     * @param name 流程名称
     * @param userIds 需要发送给的用户集合
     * @param code 流程编码
     * @param creator 流程创建人id
     * @param creatorName 流程创建人姓名
     * @param msg 消息内容
     */
    public static void sendMessage(List<Task> list, String name, List<String> userIds,String code,String creator,String creatorName,String businessId, String msg) {
        if(StringUtils.isBlank(msg)) {
            msg = creatorName + "发起了【" + name + "】，请及时处理";
        }

        //发送企业微信
        for (Task t : list) {
            UnifyApiUtils.push2Unify("WXWORK",t,name,new ArrayList<>(userIds),code,creator,creatorName,businessId, msg);
        }

        //发送站内信
        List<SysUser> sysUserList = userService.selectListByIds(userIds);
        List<String> userNameList = StreamUtils.toList(sysUserList, SysUser::getUserName);
        // 业务数据
        TaskVo businessInfo = actTaskMapper.selectBusinessInfoByBusinessKey(code);
        List<MessageInfoDO> myMsgList = messageInfoService.getMsgList(
                "项目名称：" + businessInfo.getProjectName() +  "，审批提醒",
                msg,
                Long.valueOf(code),
                "engineering_approve",
                userNameList
        );
        messageInfoService.sendMsgBatch(myMsgList);
    }

    /**
     *
     * @param list 任务
     * @param name 流程名称
     * @param userIds 需要发送给的用户集合
     * @param code 流程编码
     * @param creator 流程创建人id
     * @param sendType 发送类型 add 创建任务   set 代办变已办   del删除任务
     * @param creatorName 流程创建人姓名
     */
    public static void processTask2Unify(List<Task> list, String name, List<String> userIds,String sendType,String code,String creator,String creatorName,String businessId)
    {
        for (Task t : list) {
            String msg = creatorName + "发起了【" + name + "】，请及时处理";
            UnifyApiUtils.push2Unify(sendType,t,name,new ArrayList<>(userIds),code,creator,creatorName,businessId,msg);
        }
    }

    /**
     * 根据任务id查询 当前用户的任务，检查 当前人员 是否是该 taskId 的办理人
     *
     * @param taskId 任务id
     * @return 结果
     */
    public static Task getTaskByCurrentUser(String taskId) {
        TaskQuery taskQuery = QueryUtils.taskQuery();
        taskQuery.taskId(taskId).taskCandidateOrAssigned(String.valueOf(SecurityUtils.getUserId()));

        // todo 获取当前用户角色(需要测试)
        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
        if (CollUtil.isNotEmpty(roles)) {
            List<String> groupIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId()));
            taskQuery.taskCandidateGroupIn(groupIds);
        }
        return taskQuery.singleResult();
    }
}
